package cn.rokhdelar.umbrsapi.core.jpa.nativesql;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * SQL的模式
 *   UPDATE  .. (LEFT|RIGHT|INNER) JOIN .. ON .. SET... WHERE ....
 */
public class UpdateHelper {
 
    /**
     *  替换掉SQL注入的那些字符 ['|;|--| and | or ]
     */
    private static final String SQL_INJECT_CHARS = "([';]+|(--)+|(\\s+([aA][nN][dD])\\s+)+|(\\s+([oO][rR])\\s+)+)";
    /**
     *  开头是否包含关键字Update[不算空格],没有就加上
     */
    private static final Pattern INCLUDE_UPDATE = Pattern.compile("^(\\s*[uU][pP][dD][aA][tT][eE])+.*");
 
    /**
     *  开头是否包含FROM关键字[不算空格],没有就加上
     */
    private static final Pattern INCLUDE_FROM   = Pattern.compile("^(\\s*[fF][rR][oO][mM])+.*");
    /**
     *  SQL语句的关键字
     */
    private static final String KW_UPDATE       = "UPDATE ";
    private static final String KW_SET         = " SET ";
    private static final String KW_ON           = " ON ";
    private static final String KW_LEFT_JOIN    = " LEFT JOIN ";
    private static final String KW_RIGHT_JOIN   = " RIGHT JOIN ";
    private static final String KW_INNER_JOIN   = " INNER JOIN ";
    private static final String KW_WHERE        = " WHERE ";
    private static final String KW_AND          = " AND ";
    private static final String COMMA           = " , ";
    private static final String BLANK           = " ";
    private static final String QUOTE           = "'";
    private static final String LEFT_BRAKET     = " ( ";
    private static final String RIGHT_BRAKET    = " ) ";
    private static final String KW_IN           = " IN ";
 
    /**
     * update语句
     */
    private StringBuilder       updateClause = new StringBuilder();
    /**
     * set语句
     */
    private StringBuilder       setClause = new StringBuilder();
    
    /**
     * where子句
     */
    private StringBuilder whereClause   = new StringBuilder();

    /**
     * 参数列表
     */
    private List<Object> parameters;
    /**
     * map类型的参数
     */
    private Map<String , Object> mapParameters;
 
    /**
     * 用于一张表的情况，生成From子句
     * from topic t
     */
    public UpdateHelper(String update, String tableName, String alias){
        updateClause.append(addUpdateIfNecessary(update)).append(tableName).append(BLANK).append(alias);
    }
 
    /**
     * 用于两张表联合的情况，生成From子句，类似from table1 a,table2 b 然后添加where条件
     * 另外像left join 这种可以写在一个from字句中或者使用 leftJoin rightJoin innerJoin方法
     */
    public UpdateHelper(String update, String... froms){
        updateClause.append(addUpdateIfNecessary(update)).append(join(COMMA, " ", froms));
    }
 
    /**
     * 添加left join子句
     * @param joinClause LEFT JOIN 子句
     * @param on on条件 有一个添加在后面 , 不要带 ON 了 , 没有必须使用on方法添加
     */
    public UpdateHelper leftJoin(String joinClause , String on){
    	updateClause.append(KW_LEFT_JOIN).append(joinClause);
    	updateClause.append(join(BLANK, KW_ON, on));
        return this;
    }
    public UpdateHelper leftJoin(String joinClause){
    	updateClause.append(KW_LEFT_JOIN).append(joinClause);
        return this;
    }
    /**
     * 添加right join子句
     * @param joinClause RIGHT JOIN 子句
     */
    public UpdateHelper rightJoin(String joinClause , String on){
    	updateClause.append(KW_RIGHT_JOIN).append(joinClause);
    	updateClause.append(join(BLANK, KW_ON , on));
        return this;
    }
    public UpdateHelper rightJoin(String joinClause){
    	updateClause.append(KW_RIGHT_JOIN).append(joinClause);
        return this;
    }
    /**
     * 添加inner join子句
     * @param joinClause INNER JOIN 子句
     */
    public UpdateHelper innerJoin(String joinClause , String on){
    	updateClause.append(KW_INNER_JOIN).append(joinClause);
    	updateClause.append(join(BLANK, KW_ON , on));
        return this;
    }
    public UpdateHelper innerJoin(String joinClause){
    	updateClause.append(KW_INNER_JOIN).append(joinClause);
        return this;
    }
 
    /**
     * 添加on子句 , 不要带ON 了 , 可以被 left、right、inner join子句使用  , 但是必须紧跟在JOIN 子句后面
     * @param onClause ON 子句
     */
    public UpdateHelper on(String onClause){
    	updateClause.append(KW_ON).append(onClause);
        return this;
    }
 
    private String addUpdateIfNecessary(String update) {
        if(INCLUDE_UPDATE.matcher(update).matches()){
            //包含了Update
            return update;
        }else {
            //没有包含Update
            return KW_UPDATE + update;
        }
    }
 
    private static String join(String separator , String prefix , String... parts){
        if(null == parts || 0 == parts.length){ throw new IllegalArgumentException("parts must more than one");}
        StringBuilder buffer = new StringBuilder(prefix).append(BLANK);
        for(String part : parts){
            buffer.append(part).append(separator);
        }
        int i = buffer.lastIndexOf(separator);
        //去掉最后的separator
        return buffer.substring(0 , i);
    }
 
    public UpdateHelper addSetion(String condition){
        // 拼接条件
        addSet(condition);
        return this;
    }
    
    /**
     * 拼接where子句 d.id between ? and ? d.parent=? d.parent is null
     *
     * @param condition 具体条件
     * @param params 参数,QueryHelper只支持？参数，如果你想用Query的具名参数，就不要设置参数，产生{Query}后再调用setParameter设置
     */
    public UpdateHelper addSetion(String condition, Object... params){
        // 拼接条件
    	addSet(condition);
 
        // 添加参数
        addParams(params);
 
        return this;
    }
    /**
     * @param append 是否拼装此条件
     * @param condition 具体条件
     * @param params 参数
     */
    public UpdateHelper addSetion(boolean append, String condition, Object... params){
        if(append){
        	addSetion(condition, params);
        }
        return this;
    }
    
    private void addSet(String condition) {
        // 拼接
        if(setClause.length() == 0){
            setClause = new StringBuilder(KW_SET).append(condition);
        } else{
        	setClause.append(COMMA).append(condition);
        }
    }
    
    
    public UpdateHelper addCondition(String condition){
        // 拼接条件
        addWhere(condition);
        return this;
    }
 
    /**
     * 拼接where子句 d.id between ? and ? d.parent=? d.parent is null
     *
     * @param condition 具体条件
     * @param params 参数,QueryHelper只支持？参数，如果你想用Query的具名参数，就不要设置参数，产生{Query}后再调用setParameter设置
     */
    public UpdateHelper addCondition(String condition, Object... params){
        // 拼接条件
        addWhere(condition);
 
        // 添加参数
        addParams(params);
 
        return this;
    }
 
    /**
     * @param append 是否拼装此条件
     * @param condition 具体条件
     * @param params 参数
     */
    public UpdateHelper addCondition(boolean append, String condition, Object... params){
        if(append){
            addCondition(condition, params);
        }
        return this;
    }
 
    /**
     * @param append 是否拼装此条件
     * @param condition 具体条件
     * @param keyValue 参数
     */
    public UpdateHelper addMapCondition(boolean append, String condition, Object... keyValue){
        if(append){
            addMapCondition(condition, keyValue);
        }
        return this;
    }
 
    /**
     * 主要是为了支持某些框架中的具名参数
     * @param condition 具体条件
     * @param keyValue 模式k1,v1,k2,v2...(k1,k2必须是String)
     */
    public UpdateHelper addMapCondition(String condition, Object... keyValue){
        // 拼接参数
        addWhere(condition);
 
        //添加map类型参数k1,v1,k2,v2...
        addMapParams(keyValue);
 
        return this;
    }
 
    private void addWhere(String condition) {
        // 拼接
        if(whereClause.length() == 0){
            whereClause = new StringBuilder(KW_WHERE).append(condition);
        } else{
            whereClause.append(KW_AND).append(condition);
        }
    }
 
    /**
     * addIn("d.id" , 1,2,3) - > d.id IN (1,2,3)
     * addIn("d.phone" , "1","2","3") - > d.id IN ('1','2','3')
     * @param what 添加 IN 语句
     * @param ins In条件
     */
    public <T> UpdateHelper addIn(String what , T... ins){
        if(null == ins || ins.length == 0){
            throw new IllegalArgumentException("必须至少包含一个in条件");
        }
        // 拼接
        if(whereClause.length() == 0){
            whereClause = new StringBuilder(KW_WHERE);
        } else{
            whereClause.append(KW_AND);
        }
        // 添加左括号
        whereClause.append(what).append(KW_IN).append(LEFT_BRAKET);
        for(Object part : ins){
            //数字不需要'' , 其他就转化为字符串并加上''
            String x = part instanceof Number ? part.toString() : QUOTE + part + QUOTE;
            whereClause.append(x).append(COMMA);
        }
        // 去掉最后的 ,
        whereClause = new StringBuilder(whereClause.substring(0 , whereClause.lastIndexOf(COMMA)));
        //添加右括号
        whereClause.append(RIGHT_BRAKET);
 
        return this;
    }
 
   
    

 
 
    private void addMapParams(Object... keyValue) {
        if(0 != (keyValue.length % 2)){
            throw new IllegalArgumentException("参数必须符合模式k1,v1,k2,v2...");
        }
        if(null == mapParameters){
            mapParameters = new LinkedHashMap<>();
        }
        int kvLen = keyValue.length / 2;
        for (int i = 0; i < kvLen; i++) {
            mapParameters.put(keyValue[i].toString() , keyValue[i+1]);
        }
    }
    private void addParams(Object... params) {
        // 参数
        if(params != null){
            if(null == parameters){
                parameters = new LinkedList<>();
            }
            for(Object p : params){
                if(p != null){
                    parameters.add(p);
                }
            }
        }
    }
 

 
    public String getUpdate(){
        return updateClause.toString();
    }
 
    /**
     * From后面的所有语句 , 处理了 ? 参数的
     * @see UpdateHelper#getSqlExceptUpdateWithoutPadding()
     */
    public String getSqlExceptUpdate(){
        return paddingParam(getSqlExceptUpdateWithoutPadding() ,parameters);
    }
 
    /**
     * From后面的所有语句 , 没有处理 ?
     * @see UpdateHelper#getSqlExceptUpdate()
     */
    public String getSqlExceptUpdateWithoutPadding(){
        return updateClause.append(whereClause).toString();
    }
 
    /**
     * 获取最终拼装的SQL , 并且处理了 ? 参数的
     * @see UpdateHelper#getSqlWithoutPadding
     */
    public String getSql(){
        return paddingParam(getSqlWithoutPadding() , parameters);
    }
 
    /**
     * 获取最终拼装的SQL , 没有处理 ?
     * @see UpdateHelper#getSql()
     */
    public String getSqlWithoutPadding(){
        return new StringBuilder("").append(updateClause).append(setClause).append(whereClause).toString();
    }
 
    private String paddingParam(String sql , List<Object> params) {
        // 填充参数
        if(params != null){
            for(int i = 0 , size = params.size(); i < size; i++){
                // 1.巧妙利用替换一次之后，后面的?就自动往前移动一位，那么replaceFirst每次替换的就是下一个?
                // 2.去掉某些特殊符号，防注入
                String param = (parameters.get(i) instanceof Number) ? parameters.get(i) + "" :
                        QUOTE + parameters.get(i).toString().replaceAll(SQL_INJECT_CHARS, "")
                        + QUOTE;
                sql = sql.replaceFirst("\\?", param);
            }
        }
        return sql;
    }
    
    /**
     * 获取SQL中的参数值列表，List返回
     * @see UpdateHelper#addCondition(String)
     * @see UpdateHelper#addMapHaving(String, Object...)
     */
    public List<Object> getListParameters(){
        if(null == parameters){
            return new LinkedList<>();
        }
        return parameters;
    }
 
    /**
     * 获取SQL中的参数值列表，Array返回
     * @see UpdateHelper#addCondition(String)
     */
    public Object[] getArrayParameters(){
        if(null == parameters){
            return new Object[0];
        }
        return parameters.toArray();
    }
 
    /**
     * 获取SQL中的参数值列表，Map返回
     * @see UpdateHelper#addMapCondition(String, Object...)
     * @see UpdateHelper#addMapHaving(String, Object...)
     */
    public Map<String, Object> getMapParameters() {
        if(null == mapParameters){
            return new LinkedHashMap<>();
        }
        return mapParameters;
    }
 
    @Override
    public String toString() {
        return "QueryHelper{" +
                "sql=" + getSql() +
                ", parameters=" + getListParameters() +
                ", mapParameters=" + getMapParameters() +
                '}';
    }
}
